#include <iostream>
#include <string.h>
#include <stdio.h>

using std::cout;
using std::endl;
using std::string;

//猜想：
//char *ptr，ptr自己8字节，ptr指向的字符串只有一字节，因为char只有一字节
//后面对字符串的操纵，都是通过这个 ”头“ 

void test(){
//1、
    const char *a="hello";
    const char *b="hello";
    //首先，一定要保证const左值才能绑定字符串常量右值
    
    //c++中，hello是一个字符串常量，放在内存某个位置
    //关键是，变量a具有两面性，
    //当需要它是字符串时就是字符串,
    //需要它是地址时就是（初始时指向字符串首个字符）地址
    //用%p控制得到字符串的地址，%s得到字符串本身，
    
/*
    C++中的cout对象针对char*类型的指针有一些特殊的处理方式。
    当你使用cout << a输出一个char*类型的指针时，
    它会被视为指向以空字符结尾的C风格字符串，即以空字符\0结尾的字符数组，而不是普通的指针。

    因此，当你使用 cout<<a 输出char*类型的指针时，
    它会将指针所指向的字符数组作为字符串来处理，而不是直接输出地址值。
    这就是为什么使用cout << a，得到的是字符串而不是地址值。

    如果你想要输出字符串的地址值，可以将a指针强制转换为void*类型，
    cout<<static_cast<void*>(const_cast<char*>(a))<<endl;
    可见hello位于文字常量区
*/
    //&a无论在哪里，得到的都是a指针本身的地址，在栈区
    //*a得到的是字符串的第一个字符，所以可以理解为：a就是第一个字符的地址，
    //这也就反映了，char类型占一字节大小，所以char*指针只能操纵一字节的“头”
    
    cout<<a<<endl;  //hello
    cout<<*a<<endl; //h
    cout<<static_cast<void*>(const_cast<char*>(a))<<endl;   //hello的地址，文字常量区
    cout<<&a<<endl; //指针a的地址

    cout<<endl;
    printf("%s\n",a);//hello。这句话在c语言中不行
    printf("%c\n",*a);//h
    printf("%p\n",a);//hello的地址，也是h的地址，文字常量区
    printf("%p\n",&a);//指针a的地址

    cout<<endl;
    cout<<strlen(a)<<endl;//5
    //cout<<strlen(*a)<<endl;   strlen传入的是s字符串，不是单个c字符

    cout<<endl;
    while(*a!='\0'){
        printf("%c\n",*a);
        a++;
    }

    cout<<"~~~~~~~~~~~~~~~~~~~~~数组arr~~~~~~~~~~~~~~~~~~~~~~~~~~~~"<<endl;
//2、
//arr和上面的a一样，具有两面性
//当需要它是地址时，arr == &arr[0] == &arr
//当需要它是字符串时，arr == wuhanwangdao，arr[0] == w
    
//除了&arr+1是飞跃数组，其他&+1都是往后一个元素的地址
//除了arr可以直接当做地址，其他都都要&才是地址，不然都是下标访问
//这也证明了，一个字符w、u，占一个字节而不是一比特，
//作为对比，int数组+1就是地址+4，即4字节
    char arr[13]="wuhanwangdao";

    //int arr[5]={1,2,3,4,5};
    //printf("%p\n",arr); // 1的地址
    //printf("%p\n",arr+1);   // 2的地址=1的地址+4

    //以下都是地址
    printf("%p\n",arr); // 1的地址
    printf("%p\n",arr+1);   // 2的地址
    printf("%p\n",&arr); // 1的地址
    printf("%p\n",&arr[0]+1); // 2的地址
    printf("%p\n",&arr[0]); // 1的地址
    printf("%p\n",&arr+1);  //越过数组

    cout<<endl;
    printf("%p\n",&arr[1]); // 2的地址
    printf("%p\n",&arr[1]+1);  //3的地址

    cout<<endl;
    printf("%c\n",arr[0]);    //w
    printf("%c\n",arr[0]+1);    //w+1=x
    printf("%d\n",arr[0]+1);    //119+1=120
    cout<<arr[0]<<endl;   //w
    cout<<arr[0]+1<<endl;   //119+1=120

    cout<<endl;
    cout<<arr<<endl;
    printf("%s\n",arr);
    cout<<&arr<<endl;

    cout<<&arr[0]<<endl;
    //当你使用 &arr[0] 时，它返回的是数组 arr 的第一个元素的地址，即数组的起始地址，
    //它与 arr 是等价的，&arr[0] == arr。
    //即 cout<<&arr[0]<<endl == cout<<arr<<endl  输出 "wuhan"。

    //猜测：
    //int arr[10]={···}，其实arr就是 int * const arr，即常量指针
    //arr不能改变指向，但是可以改变内容，比如arr[0]=6
    //这也就解释了为什么arr只管第一个int
    //但是它又重载了其他运算符，比如[]、<<、&，才让它看起来像数组
    
    cout<<"~~~~~~~~~~~~~~~~~~~~string~~~~~~~~~~~~~~~~~~~~~~~~~~~~"<<endl;
//3、
    string str="helloworldaaaaaaaaaaaaaaaaaaa";
    cout<<str<<endl;    //hello
    cout<<&str<<endl;   //1900，str对象本身的地址？
    cout<<str[0]<<endl; //h
    cout<<str.c_str()<<endl;    //hello

    //str是string的对象，不是什么指针
    //只有当string使用了c.str()提取c风格的字符串时
    //（无论是buff中的*_pointer,还是_local[16]），才能使用printf
    // printf("%s\n",str);  printf不认识string对象str
    printf("%s\n",str.c_str()); //hello
    printf("%p\n",str.c_str()); 
    //当字符串较短时，1910，和str都在栈区
    //当字符串较长时，可以看见此时字符串在堆区

    cout<<endl;
    int num=100;
    int *q=&num;
    cout<<sizeof(q)<<endl;  //8字节
    cout<<sizeof(num)<<endl;    //4字节
    cout<<sizeof(char)<<endl;    //1字节

    cout<<endl;
    cout<<sizeof(b)<<endl;//char *，8字节
    cout<<strlen(b)<<endl;  //hello不包括\0，5字节
    cout<<sizeof(arr)<<endl;//整个数组大小，包括\0, 13字节
    cout<<strlen(arr)<<endl;//字符串长度，12字节
    cout<<sizeof(str)<<endl;//sso，32字节
    cout<<strlen(str.c_str())<<endl;//字符串长度，10
}

int main()
{   
    test();
    return 0;
}

